package com.whatisjee.ssf.domain.service.impl;

import static com.whatisjee.ssf.misc.SecurityUtils.createPasswordHash;

import java.util.Date;
import java.util.List;

import com.whatisjee.ssf.domain.dao.HelperDao;
import com.whatisjee.ssf.domain.dao.LogDao;
import com.whatisjee.ssf.domain.dao.RoleDao;
import com.whatisjee.ssf.domain.dao.UserDao;
import com.whatisjee.ssf.domain.entity.Log;
import com.whatisjee.ssf.domain.entity.Role;
import com.whatisjee.ssf.domain.entity.User;
import com.whatisjee.ssf.domain.service.AppService;
import com.whatisjee.ssf.exception.BusinessException;
import com.whatisjee.ssf.exception.NoRollbackBusinessException;
import com.whatisjee.ssf.misc.Constants;
import com.whatisjee.ssf.misc.ErrorMessage;
import com.whatisjee.ssf.misc.LogCriteria;
import com.whatisjee.ssf.misc.Page;
import com.whatisjee.ssf.misc.PagingList;
import com.whatisjee.ssf.misc.UserCriteria;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Stateless(name = "appService")
public class AppServiceImpl implements AppService {
	@Inject
	private HelperDao helperDao;
	
	@Inject
	private UserDao userDao;
	
	@Inject
	private RoleDao roleDao;
	
	@Inject
	private LogDao logDao;

	@Override
	public List<String> findDistincts(String entityName, String propName) {
		return helperDao.selectDistincts(entityName, propName);
	}

	@Override
	public User authenticate(String name, String password)
			throws NoRollbackBusinessException {
		name = name == null ? null : name.toUpperCase();
		
		User user = userDao.select(name);
		if(user == null) {
			log(Constants.LOG_ERROR, "log.auth.notFound", null, name);
			throw NoRollbackBusinessException.build(ErrorMessage.createGlobalMessage("user.notFound", name)); 
		}
		
		String pwsHash = createPasswordHash(name + password);
		if(!pwsHash.equals(user.getPassword())) {
			log(Constants.LOG_ERROR, "log.auth.wrongPwd", null, name);
			throw NoRollbackBusinessException.build(ErrorMessage.createGlobalMessage("user.wrongPwd"));
		}
		
		if(!user.isEnabled()) {
			log(Constants.LOG_ERROR, "log.auth.disabled", null, name);
			throw NoRollbackBusinessException.build(ErrorMessage.createGlobalMessage("user.disabled", name));
		}
		
		log(Constants.LOG_INFO, "log.auth.success", null, name);
		return user;
	}

	@Override
	public List<Role> findRoles() {
		return roleDao.select();
	}

	@Override
	public User saveUser(User user) throws BusinessException {
		List<Role> roles = user.getRoles();
		if(roles == null || roles.isEmpty()) {
			throw BusinessException.build(ErrorMessage.createGlobalMessage("user.noRole"));
		}
		
		if(user.getId() == null) {
			User _user = userDao.select(user.getName());
			if(_user != null) {
				throw BusinessException.build(ErrorMessage.createMessageFor("itName", "cmm.duplicated"));
			}
		}
		
		if(user.getId() == null || user.isPwdChged()) {
			if(user.getOldPwd() != null) {
				User _user = userDao.select(user.getId());
				String pwdHash = createPasswordHash(user.getName() + user.getOldPwd());
				if(!pwdHash.equals(_user.getPassword())) {
					throw BusinessException.build(ErrorMessage.createMessageFor("isOldPwd", "user.wrongPwd"));
				}
				helperDao.evict(_user);
			}
			String pwdHash = createPasswordHash(user.getName() + user.getPassword());
			user.setPassword(pwdHash);
		}
		
		boolean update = user.getId() != null;
		userDao.save(user);
		String log = update ? "log.mdfUser" : "log.addUser";
		log(Constants.LOG_INFO, log, null);
		return user;
	}

	@Override
	public PagingList<User> findUsers(UserCriteria criteria, Page page) {
		int count = userDao.selectCount(criteria);
		List<User> list = userDao.select(criteria, page);
		return (new PagingList<User>(count, list));
	}

	@Override
	public User findUser(Integer id) {
		return userDao.select(id);
	}

	@Override
	public PagingList<Log> findLogs(LogCriteria criteria, Page page) {
		int count = logDao.selectCount(criteria);
		List<Log> list = logDao.select(criteria, page);
		return (new PagingList<Log>(count, list));
	}

	@Override
	public Log log(Short severity, String summary, String detail, Object... args) {
//		User user = getCurrentUser(); // TODO: Correct the implimentation
                User user = new User();
                user.setName("ADMIN");
                user.setTerminal("127.0.0.1");
		return log(user, severity, summary, detail, args);
	}

	@Override
	public Log log(User user, Short severity, String summary, String detail,
			Object... args) {
		Log log = new Log();
		log.setLoggedAt(new Date());
		log.setArgs(args);
		log.setUsername(user.getName());
		log.setTerminal(user.getTerminal());
		log.setSeverity(severity);
		log.setSummary(summary);
		log.setDetail(detail);
		logDao.save(log);
		return log;
	}

}